package byx.container.annotation.extension.impl;

import byx.container.annotation.annotation.Wrap;
import byx.container.annotation.extension.core.AnnotationMetadata;
import byx.container.annotation.extension.core.ClassPostProcessor;
import byx.container.annotation.extension.core.ClassPostProcessorContext;
import byx.container.core.Container;
import byx.container.core.ObjectFactory;
import byx.container.core.RegisterInfo;
import byx.container.core.utils.CastUtils;
import byx.container.core.utils.ReflectUtils;

import java.util.Arrays;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;

import static byx.container.core.ObjectFactories.all;
import static byx.container.core.ObjectFactories.fromContainer;

/**
 * 处理Wrap注解
 */
public class WrapProcessor implements ClassPostProcessor {
    @Override
    public ObjectFactory<?> postProcess(ClassPostProcessorContext ctx) {
        RegisterInfo registerInfo = ctx.getRegisterInfo();
        ObjectFactory<?> factory = registerInfo.getFactory();
        AnnotationMetadata annotationMetadata = ctx.getAnnotationMetadata();
        Container container = ctx.getContainer();

        if (!annotationMetadata.isAnnotationPresent(Wrap.class)) {
            return factory;
        }

        Wrap wrap = annotationMetadata.getAnnotation(Wrap.class);
        List<Class<Function<?, ?>>> wrapperTypes = CastUtils.cast(Arrays.stream(wrap.value())
                .collect(Collectors.toList()));

        if (wrap.fromContainer()) {
            List<ObjectFactory<Function<?, ?>>> factories = CastUtils.cast(wrapperTypes.stream()
                    .map(t -> fromContainer(container, t))
                    .collect(Collectors.toList()));
            return all(factories)
                    .bind(wrappers -> factory.map(CastUtils.cast(composeWrappers(wrappers))));
        } else {
            List<Function<?, ?>> wrappers = wrapperTypes.stream()
                    .map(ReflectUtils::createByDefaultConstructor)
                    .collect(Collectors.toList());
            return factory.map(CastUtils.cast(composeWrappers(wrappers)));
        }
    }

    private Function<?, ?> composeWrappers(List<Function<?, ?>> wrappers) {
        Function<?, ?> wrapper = Function.identity();
        for (Function<?, ?> w : wrappers) {
            wrapper = wrapper.andThen(CastUtils.cast(w));
        }
        return wrapper;
    }
}
